package org.codefilarete.tool.collection;

import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * An {@link Iterator} that iterates over other given {@link Iterator}s
 * 
 * @author Guillaume Mary
 */
public class IteratorIterator<E> implements Iterator<E> {
	
	private final Iterator<Iterable<E>> iterables;
	
	private Iterator<E> currentIterator;
	
	@SafeVarargs // method body doesn't handle improperly varargs parameter so it would generate ClassCastException
	public IteratorIterator(Iterator<E> ... iterables) {
		// we wrap given iterator array into an array of iterables, no very cleanly done but it's a very local usage.
		this(new Iterator<Iterable<E>>() {
			
			private final Iterator<Iterator<E>> delegate = new ArrayIterator<>(iterables);
			
			@Override
			public boolean hasNext() {
				return delegate.hasNext();
			}
			
			@Override
			public Iterable<E> next() {
				return delegate::next;
			}
		});
	}
	
	@SafeVarargs // method body doesn't handle improperly varargs parameter so it would generate ClassCastException
	public IteratorIterator(Iterable<E> ... iterables) {
		this(new ArrayIterator<>(iterables));
	}
	
	public IteratorIterator(Iterable<Iterable<E>> iterables) {
		this(iterables.iterator());
	}
	
	public IteratorIterator(Iterator<Iterable<E>> iterables) {
		this.iterables = iterables;
	}
	
	@Override
	public boolean hasNext() {
		if (currentIterator == null || !currentIterator.hasNext()) {
			boolean found = false;
			while(!found) {
				if (iterables.hasNext()) {
					currentIterator = iterables.next().iterator();
					found = currentIterator.hasNext();
				} else {
					return false;
				}
			}
		}
		// here currentIterator != null && currentIterator.hasNext() or found = true => both conditions are always true
		return true;
	}
	
	@Override
	public E next() {
		if (!hasNext()) {
			throw new NoSuchElementException();
		}
		return currentIterator.next();
	}
	
	@Override
	public void remove() {
		currentIterator.remove();
	}
}
